﻿using System;
using System.Net.Sockets;
using System.Threading;

namespace SiMay.Sockets.Tcp.Awaitable
{
    /// <summary>
    /// 异步操作等待
    /// </summary>
    public sealed class SaeaAwaiter : IDisposable
    {
        /// <summary>
        /// 异步套接字操作
        /// </summary>
        private readonly SocketAsyncEventArgs _saea = new SocketAsyncEventArgs();

        private static readonly Action<SaeaAwaiter, SocketError> SENTINEL = delegate { };

        private Action<SaeaAwaiter, SocketError> _continuation;

        public SocketAsyncEventArgs Saea
        {
            get { return _saea; }
        }

        internal SaeaAwaiter()
        {
            //异步操作完成时执行 OnSaeaCompleted 方法
            _saea.Completed += OnSaeaCompleted;
        }

        /// <summary>
        /// 异步操作完成触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnSaeaCompleted(object sender, SocketAsyncEventArgs args)
        {
            this.CompletedHandler();
        }

        /// <summary>
        /// Socket的异步方法执行完成的时候执行回调
        /// </summary>
        internal void CompletedHandler()
        {
            var continuation = _continuation ?? Interlocked.CompareExchange(ref _continuation, SENTINEL, null);

            if (continuation != null)
            {
                continuation.Invoke(this, _saea.SocketError);
            }
        }

        /// <summary>
        /// 将要回调的方法存储到_continuation
        /// </summary>
        /// <param name="continuation">回调方法</param>
        internal void OnCompleted(Action<SaeaAwaiter, SocketError> continuation)
        {
            if (_continuation == SENTINEL || Interlocked.CompareExchange(ref _continuation, continuation, null) == SENTINEL)
            {
                continuation.Invoke(this, _saea.SocketError);
            }
        }

        /// <summary>
        /// 把异步操作对象触发的事件方法赋为空
        /// </summary>
        internal void Reset()
        {
            _continuation = null;
        }

        /// <summary>
        /// 释放对象
        /// </summary>
        public void Dispose()
        {
            _saea.Dispose();
            _continuation = null;
        }
    }
}
